package com.appistry.management.domain.impl;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.appistry.api.rest.domain.RestResponse;
import com.appistry.management.Manifest;
import com.appistry.management.ManifestEntry;
import com.appistry.management.State;
import com.appistry.management.Worker;
import com.appistry.management.WorkerInfo;
import com.appistry.management.WorkerOption;
import com.appistry.management.WorkerPerformanceInfo;
import com.appistry.management.domain.AbstractRestDomainObject;
import com.appistry.management.domain.ControllerAction;
import com.appistry.management.exception.FabricManagerException;
import com.appistry.management.rest.RestFabricManager;
import com.appistry.management.util.JsonTransformUtil;

public class WorkerImpl extends AbstractRestDomainObject implements Worker {
	private RestFabricManager restFabricManager;
	private InetAddress address;

	public WorkerImpl(RestFabricManager restFabricManager, InetAddress address) {
		this.restFabricManager = restFabricManager;
		this.address = address;
	}

	public InetAddress getInetAddress() {
		return address;
	}

	public String getIpAddress() {
		return address.getHostAddress();
	}

	public Map<String, String> getWorkerAddrCfg() {
		// TODO implement method
		throw new UnsupportedOperationException(NOT_IMPLEMENT);
	}

	public WorkerInfo getWorkerInfo() throws FabricManagerException{
		try {
			RestResponse restResponse = restFabricManager.getWorkerInfo(address);
			checkForErrors(restResponse);

			WorkerInfo info = JsonTransformUtil.toWorkerInfo(restResponse.getBodyAsString());
			
			return info;
		} catch (JSONException e) {
			throw new FabricManagerException(PARSE_JSON_ERR, e);
		} catch (Exception e) {
			throw new FabricManagerException(e);
		}
	}

	public int getWorkerKeeperUpcount() throws FabricManagerException {
		try {
			RestResponse restResponse = restFabricManager.getWorkerKeeperUpcount(address);
			checkForErrors(restResponse);

			JSONObject jsonObject = new JSONObject(restResponse.getBodyAsString());

			return jsonObject.getInt("upcount");
		} catch (JSONException e) {
			throw new FabricManagerException(PARSE_JSON_ERR, e);
		} catch (Exception e) {
			throw new FabricManagerException(e);
		}
	}

	public Manifest getWorkerManifest() throws FabricManagerException {
		try {
			RestResponse restResponse = restFabricManager.getWorkerManifest(address);
			checkForErrors(restResponse);

			Manifest manifest = JsonTransformUtil.toManifest(restResponse.getBodyAsString());
			
			return manifest;
		} catch (JSONException e) {
			throw new FabricManagerException(PARSE_JSON_ERR, e);
		} catch (Exception e) {
			throw new FabricManagerException(e);
		}
	}

	public ManifestEntry getWorkerManifestEntry(String entry) throws FabricManagerException {
		try {
			RestResponse restResponse = restFabricManager.getWorkerManifestEntry(address, entry);
			checkForErrors(restResponse);
			
			ManifestEntry manifestEntry = JsonTransformUtil.toManifestEntry(restResponse.getBodyAsString());
			
			return manifestEntry;
		} catch (JSONException e) {
			throw new FabricManagerException(PARSE_JSON_ERR, e);
		} catch (Exception e) {
			throw new FabricManagerException(e);
		}
	}

	public List<ManifestEntry> getWorkerManifestEntryList() throws FabricManagerException {
		// TODO change to not get the whole thing at once.. make it smarter
		try {
			RestResponse restResponse = restFabricManager.getWorkerManifestEntryList(address);
			checkForErrors(restResponse);

			ArrayList<ManifestEntry> entries = new ArrayList<ManifestEntry>();
			JSONArray jsonArray = new JSONArray(restResponse.getBodyAsString());
			for (int i = 0; i < jsonArray.length(); i++) {
				JSONObject jsonObj = jsonArray.getJSONObject(i);
				String entry = jsonObj.getString("entry");
				entries.add(getWorkerManifestEntry(entry));
			}

			return entries;
		} catch (JSONException e) {
			throw new FabricManagerException(PARSE_JSON_ERR, e);
		} catch (Exception e) {
			throw new FabricManagerException(e);
		}
	}

	public WorkerPerformanceInfo getWorkerPerformanceInfo() throws FabricManagerException {
		try {
			RestResponse restResponse = restFabricManager.getWorkerPerformanceInfo(address);
			checkForErrors(restResponse);
			
			WorkerPerformanceInfo info = JsonTransformUtil.toWorkerPerformanceInfo(restResponse.getBodyAsString());

			return info;
		} catch (JSONException e) {
			throw new FabricManagerException(PARSE_JSON_ERR, e);
		} catch (Exception e) {
			throw new FabricManagerException(e);
		}
	}

	public State getWorkerState() throws FabricManagerException {
		try {
			RestResponse restResponse = restFabricManager.getWorkerState(address);
			checkForErrors(restResponse);

			State state = JsonTransformUtil.toState(restResponse.getBodyAsString());

			return state;
		} catch (JSONException e) {
			throw new FabricManagerException(PARSE_JSON_ERR, e);
		} catch (Exception e) {
			throw new FabricManagerException(e);
		}
	}

	public void putWorkerAddrCfg(Map<String, String> workerAddrCfg) throws FabricManagerException {
		try {
			StringBuilder addrCfg = new StringBuilder();

			for (String key : workerAddrCfg.keySet()) {
				addrCfg.append(key);
				addrCfg.append("=");
				addrCfg.append(workerAddrCfg.get(key));
				addrCfg.append("\n");
			}
			InputStream content = new ByteArrayInputStream(addrCfg.toString().getBytes());
			RestResponse restResponse = restFabricManager.putWorkerAddrCfg(address, content);
			checkForErrors(restResponse);
		} catch (Exception e) {
			throw new FabricManagerException(e);
		}

	}

	public void putWorkerAddrCfg(Map<String, String> workerAddrCfg, WorkerOption option) throws FabricManagerException {
		try {
			StringBuilder addrCfg = new StringBuilder();

			for (String key : workerAddrCfg.keySet()) {
				addrCfg.append(key);
				addrCfg.append("=");
				addrCfg.append(workerAddrCfg.get(key));
				addrCfg.append("\n");
			}
			InputStream content = new ByteArrayInputStream(addrCfg.toString().getBytes());
			RestResponse restResponse = restFabricManager.putWorkerAddrCfg(address, content, option); 
			checkForErrors(restResponse);
		} catch (Exception e) {
			throw new FabricManagerException(e);
		}
	}

	public void reboot() throws FabricManagerException {
		workerController(address, ControllerAction.REBOOT);
	}

	public void start() throws FabricManagerException {
		workerController(address, ControllerAction.START);
	}

	public void stop() throws FabricManagerException {
		workerController(address, ControllerAction.STOP);
	}

	private boolean workerController(InetAddress ip, ControllerAction action) throws FabricManagerException {
		try {
			RestResponse restResponse = restFabricManager.workerController(ip, action);
			checkForErrors(restResponse);

			return true;
		} catch (Exception e) {
			throw new FabricManagerException(e);
		}
	}

}
